package no.mehdi.game.web;

import no.mehdi.game.domain.GameResult;
import no.mehdi.game.domain.Player;
import no.mehdi.game.service.CreatureGenerator;
import no.mehdi.game.service.GameResultGenerator;
import no.mehdi.game.service.PlayerServices;

import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.webflow.action.FormAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

/**
 * The game action object. It controls the webpage.
 * 
 * @author Mehdi Zare
 * 
 */
public class GameAction extends FormAction {
	/** The playerServices */
	private PlayerServices playerServices;
	/** The creature generator */
	private CreatureGenerator creatureGenerator;
	/** The gameResult generator */
	private GameResultGenerator gameResultGenerator;
	/** password encoder */
	private PasswordEncoder passwordEncoder;

	/**
	 * Login the user and generate a new creature by clicking log in button. If
	 * the user is already logged in it will not log in user in another session.
	 * 
	 * @param context
	 *            the {@link RequestContext}
	 * @return {@link Event}
	 * @throws Exception
	 * @{@link Exception}
	 */
	public Event login(RequestContext context) throws Exception {

		GameForm form = (GameForm) super.getFormObject(context);
		if (logger.isDebugEnabled()) {
			logger.debug("Loging in " + form.getName());
		}
		// Check the user is stored in DB.
		Player player = playerServices.retrievePlayerByName(form.getName());
		if (player != null) {

			// Check the password and then check the user is logged in already. 
			if (!passwordEncoder.isPasswordValid(player.getPassword(), form.getPassword(), null)) {
				displayMessage(context, "worng_password");
				return success();
			} else if (player.isUserLogedIn()) {
				displayMessage(context, "user_already_logged_in");
				return success();
			}

		} else {
			// If new player store the username and password
			player = playerServices.addPlayer(form.getName(), form.getPassword());
		}
		// mark the user logged in
		player.setUserLogedIn(true);
		playerServices.updatePlayerResults(player);

		// set player in the form.
		form.setPlayer(player);
		form.setUserLoggedIn(true);
		//generate a new creature.
		form.setCreature(creatureGenerator.generateCreature());
		return success();
	}

	/**
	 * 
	 * Log out the user by clicking on log out button.
	 * 
	 * @param context
	 *            the {@link RequestContext}
	 * @return {@link Event}
	 * @throws Exception
	 * @{@link Exception}
	 */
	public Event logOut(RequestContext context) throws Exception {
		if (logger.isDebugEnabled()) {
			logger.debug("Log out");
		}
		GameForm form = (GameForm) super.getFormObject(context);
		Player player = form.getPlayer();
		// Mark the user logged out in DB.
		player.setUserLogedIn(false);
		playerServices.updatePlayerResults(player);
		// reset the form.
		resetForm(context);
		return success();
	}

	/**
	 * Execute attack by clicking attack button and put the game in waiting
	 * state.
	 * 
	 * @param context
	 *            the {@link RequestContext}
	 * @return {@link Event}
	 * @throws Exception
	 * @{@link Exception}
	 */
	public Event attack(RequestContext context) throws Exception {
		if (logger.isDebugEnabled()) {
			logger.debug("Attaking! ");
		}
		GameForm form = (GameForm) super.getFormObject(context);
		GameResult result = gameResultGenerator.generateResult();
		if (result.equals(GameResult.WIN)) {
			form.addWin();
			form.setGameResult("You killed the " + form.getCreature());
			playerServices.updatePlayerResults(form.getPlayer());
		} else if (result.equals(GameResult.LOSE)) {
			form.addLoss();
			playerServices.updatePlayerResults(form.getPlayer());
			form.setGameResult("You lost the battle");
		} else if (result.equals(GameResult.DRAW)) {
			form.setGameResult("Draw");
		}

		enterWaitState(form);
		return success();
	}

	/**
	 * 
	 * Execute runaway by clicking on runaway button.
	 * 
	 * @param context
	 *            the {@link RequestContext}
	 * @return {@link Event}
	 * @throws Exception
	 * @{@link Exception}
	 */
	public Event runAway(RequestContext context) throws Exception {
		if (logger.isDebugEnabled()) {
			logger.debug("Run Away!");
		}
		GameForm form = (GameForm) super.getFormObject(context);
		enterWaitState(form);
		return success();
	}

	/**
	 * Enter the game in waiting state which is 10 seconds.
	 * 
	 * @param form
	 *            the {@link GameForm}
	 */
	private void enterWaitState(GameForm form) {
		if (logger.isDebugEnabled()) {
			logger.debug("Enter waiting state");
		}
		form.setWaiting(true);
		form.setStartWaitingTime(System.currentTimeMillis());
		form.setNextCreature(creatureGenerator.generateCreature());
	}

	/**
	 * Display error messages.
	 * 
	 * @param context
	 */
	private void displayMessage(RequestContext context, String message) {
		MessageContext messages = context.getMessageContext();
		messages.addMessage(new MessageBuilder().error().source("password").code(message).build());
	}

	/**
	 * @param playerServices
	 *            the playerServices to set
	 */
	public void setPlayerServices(PlayerServices playerServices) {
		this.playerServices = playerServices;
	}

	/**
	 * @param creatureGenerator
	 *            the creatureGenerator to set
	 */
	public void setCreatureGenerator(CreatureGenerator creatureGenerator) {
		this.creatureGenerator = creatureGenerator;
	}

	/**
	 * @param gameResultGenerator
	 *            the gameResultGenerator to set
	 */
	public void setGameResultGenerator(GameResultGenerator gameResultGenerator) {
		this.gameResultGenerator = gameResultGenerator;
	}

	/**
	 * @param passwordEncoder
	 *            the passwordEncoder to set
	 */
	public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
		this.passwordEncoder = passwordEncoder;
	}

}
